﻿#include "FITKGraphObjectMaterialOrientation.h"

// VTK
#include <vtkPoints.h>
#include <vtkUnstructuredGrid.h>
#include <vtkCellData.h>
#include <vtkProperty.h>
#include <vtkIntArray.h>
#include <vtkSelectionNode.h>
#include <vtkSelection.h>
#include <vtkExtractSelection.h>
#include <vtkIdTypeArray.h>
#include <vtkIdList.h>
#include <vtkPointSource.h>
#include <vtkGlyph3D.h>

// For VTK 7
//@{
#include <vtkCellType.h>
//@}

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data manager
#include "FITKSetSurfPolyProvider.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMaterialOrientation.h"

namespace Exchange
{
    FITKGraphObjectMaterialOrientation::FITKGraphObjectMaterialOrientation(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaMaterialOrientation* matOriData)
        : FITKGraphObjectMarkBase(caseData, matOriData)
    {
        if (!matOriData)
        {
            return;
        }

        // Initialize.
        init();

        // Generate the orientation's element set grid data and the cell center of it.
        bool flag = generatePolyData(matOriData);
        if (!flag)
        {
            return;
        }

        // Generate actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();
        
        // Save if the bounds are fixed or dynamic.
        m_hasFixedBounds = false;
    }

    FITKGraphObjectMaterialOrientation::~FITKGraphObjectMaterialOrientation()
    {
        // Delete the source.

        // Delete poly data.
        //@{
        deleteVtkObj(m_points);
        deleteVtkObj(m_ugrid);
        //@}
    }

    void FITKGraphObjectMaterialOrientation::setVisible(bool visibility)
    {
        if (m_fActor)
        {
            m_fActor->SetVisibility(visibility);
        }
    }

    void FITKGraphObjectMaterialOrientation::init()
    {
        // Create the source.

        // Create poly data.
        //@{
        m_ugrid = vtkUnstructuredGrid::New();
        m_points = vtkPoints::New();
        m_ugrid->SetPoints(m_points);
        //@}

        // Initialize the glyph size.
        m_glyphSize = 70.;
    }

    void FITKGraphObjectMaterialOrientation::initActors()
    {
        // Get the color of inertia.
        double color3[3]{ 0., 0., 0. };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_intertiaColor, color3);

        // Create the actor for glyph.
        m_fActor = FITKGraphActor::New();
        m_fActor->setGraphObject(this);
        m_fActor->SetPickable(false);
        m_fActor->SetVisibility(true);
        m_fActor->setScalarVisibility(false);
        m_fActor->GetProperty()->SetRepresentation(0);
        m_fActor->GetProperty()->SetColor(color3);
        m_fActor->GetProperty()->SetPointSize(m_glyphSize);
        addActor(m_fActor);
    }

    void FITKGraphObjectMaterialOrientation::clearData()
    {
        // Clear data.
        resetVtkObj(m_points);
        resetVtkObj(m_ugrid);

        // Reset the component id.
        m_componentId = -1;
    }

    void FITKGraphObjectMaterialOrientation::highlight(QColor color)
    {
        // If the object is invisible, then return.
        if (!getDataVisibility())
        {
            return;
        }

        // Get the inertia highlight color.
        double color3[3] = { 0 ,0, 0 };
        if (color.isValid())
        {
            FITKGraphCommons::QColorToDouble3(color, color3);
        }
        else
        {
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_intertiaHighlightColor, color3);
        }      
        
        // Set the highlight color for lines.
        if (m_fActor)
        {
            m_fActor->GetProperty()->SetColor(color3);
        }

        FITKGraphObject3D::highlight();
    }

    void FITKGraphObjectMaterialOrientation::disHighlight()
    {
        // Get the inertia default color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_intertiaColor, color3);

        // Set the default color for lines.
        if (m_fActor)
        {
            m_fActor->GetProperty()->SetColor(color3);
        }     

        FITKGraphObject3D::disHighlight();
    }

    void FITKGraphObjectMaterialOrientation::update(bool forceUpdate)
    {
        // Update poly data after the material orientation's set or rotate axis and rotate angle changed.
        Interface::FITKAbaMaterialOrientation* matOriData = dynamic_cast<Interface::FITKAbaMaterialOrientation*>(_dataObj);
        if (!m_ugrid || !matOriData)
        {
            return;
        }

        // Get the set id.    
        int compId = matOriData->getModelSetID();

        // Get the coordinate system.
        int coordId =  matOriData->getCsysID();
        Q_UNUSED(coordId);

        // Check if the component exists.
        bool isCompValid = getComponentValid(compId);

        bool needUpdate = false;
        if (!isCompValid)
        {
            needUpdate = true;

            // Clear data.
            clearData();
        }

        // Check if the locations changed.
        needUpdate |= (compId != m_componentId || forceUpdate);
        if (!needUpdate)
        {
            // Update the visibility only.
            updateVisibility();
            return;
        }

        // Clear data first.
        clearData();

        bool flag = generatePolyData(matOriData);
        if (!flag)
        {
            return;
        }

        // Update the visibility at last.
        updateVisibility();

        // Modify actors.
        updateActors();
    }

    void FITKGraphObjectMaterialOrientation::updateVisibility()
    {
        // Update the full actor.
        FITKGraphObject3D::updateVisibility();

        if (!m_ugrid)
        {
            return;
        }

        bool hasChanged = false;

        // If the visibility hasn't changed, then return.
        if (!hasChanged)
        {
            return;
        }


        // Update the data.
        m_ugrid->Modified();
    }

    void FITKGraphObjectMaterialOrientation::generateGraph()
    {
        if (!m_ugrid)
        {
            return;
        }

        // Initialize the actors.
        initActors();

        // Create glyph filter.
        m_glyph = vtkGlyph3D::New();
        // m_glyph->SetSourceConnection(->GetOutputPort());

        // Set the input data and connection.
        m_glyph->SetInputData(m_ugrid);
        m_fActor->setInputConnection(m_glyph->GetOutputPort());
    }

    bool FITKGraphObjectMaterialOrientation::generatePolyData(Interface::FITKAbaMaterialOrientation* matOriData)
    {
        if (!matOriData || !m_ugrid)
        {
            return false;
        }

        // Get the set id.
        m_componentId = matOriData->getModelSetID();

        // Try to get set or surface poly data.
        QList<int> modelIds;

        // Generate part set grid.
        vtkUnstructuredGrid* ugrid = vtkUnstructuredGrid::New();
        QList<int> subSetIds;
        bool flag = generateComponentGridData(m_componentId, ugrid, modelIds, subSetIds);
        if (!flag)
        {
            ugrid->Delete();
            return false;
        }

        // Add a point and a cell.
        auto addPoint = [&](double* position)
        {
            // Add point.
            int ptId = m_points->InsertNextPoint(position);

            // Add cell.
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptId);
            m_ugrid->InsertNextCell(VTKCellType::VTK_VERTEX, cell);
        };

        // Add points of the cell center of set grid.
        //@{
        for (int i = 0; i < ugrid->GetNumberOfCells(); i++)
        {
            double pt[3]{ 0., 0., 0. };
            FITKGraphCommons::GetCellCenter(ugrid->GetCell(i), pt);
            addPoint(pt);
        }
        //@}

        ugrid->Delete();

        return flag;
    }

    bool FITKGraphObjectMaterialOrientation::getDataVisibility()
    {
        bool isActive = false;

        // Try to down cast to material orientation data.            
        Interface::FITKAbaMaterialOrientation* matOriData = dynamic_cast<Interface::FITKAbaMaterialOrientation*>(_dataObj);
        if (matOriData)
        {
            isActive = matOriData->isEnable();
        }
        
        return isActive;
    }

    void FITKGraphObjectMaterialOrientation::simplified(bool enabled)
    {
        if (!m_graphSimplified)
        {
            return;
        }

        // Change the actor visibility while changing the camera.
        if (m_fActor && getDataVisibility())
        {
            m_fActor->SetVisibility(!enabled);
        }
    }

    bool FITKGraphObjectMaterialOrientation::isRelatedTo(int dataObjId)
    {
        // Check if the id is the self-object id.
        bool isTheObj = FITKGraphObject3D::isRelatedTo(dataObjId);
        if (isTheObj)
        {
            return true;
        }

        // Check if the location is the same as the data.
        bool hasCompRelated = m_componentId == dataObjId;
        if (hasCompRelated)
        {
            return true;
        }

        return false;
    }
}   // namespace Exchange